<?php
//require 'common.php';
//require 'useradmin.php';

define("SERVER_VERSION",     0x00094100);
define("PROTOCOL_VERSION",   0x00010D00);
define("OPTIONS_NUM",        62);

function MakeWord($a, $b){
  return $a | $b << 8;
}

function MakeInt($a, $b, $c, $d){
  $wA = MakeWord($a, $b);
  $wB = MakeWord($c, $d);
  return $wA | $wB << 16;
}

function HiWord($L){
  return $L >> 16;
}

function LoWord($L){
  return $L & 0x0000FFFF;
}

function HiByte($W){
 return $W >> 8;
}

function LoByte($W){
  return $W & 0xFF;
}

function ParserString(&$data, &$strValue){
  if(strlen($data) < 2)
    return false;

  $len = ord($data[0]) * 256 + ord($data[1]);
  if(strlen($data)-2 < $len)
    return false;
  $data = substr($data, 2);
  $strValue = substr($data, 0, $len);
  $data = substr($data, $len);
  return true;
}

function ParserInt(&$data, &$intValue){
  if(strlen($data) < 4) 
     return false;

  $intValue = MakeInt(ord($data[0]), ord($data[1]), ord($data[2]), ord($data[3]));
  $data = substr($data, 4);
}

function ParserWord(&$data, &$wordValue){
  if(strlen($data) < 2)
    return false;
  
  $wordValue = ord($data[0]) * 256 + ord($data[1]);
  $data = substr($data, 2);
}

function ParserWord2(&$data, &$wordValue){
  if(strlen($data) < 2)
    return false;

  //$wordValue = ord($data[0]) << 8 + ord($data[1]);
  $wordValue = ord($data[0]) * 256 + ord($data[1]);
  $data = substr($data, 2);
}

function ParserByte(&$data, &$byteValue){
  if(strlen($data) < 1)
    return false;

  $byteValue = ord($data[0]);
  $data = substr($data, 1);
}

function Debug($msg){
  $msg = $msg . '<br>';
  switch(func_num_args()){
  case 1:
    printf($msg);
    break;
  case 2:
    printf($msg, func_get_arg(1));
    break;
  case 3:
    printf($msg, func_get_arg(1), func_get_arg(2));
    break;
  case 4:
    printf($msg, func_get_arg(1), func_get_arg(2), func_get_arg(3));
    break;
  case 5:
    printf($msg, func_get_arg(1), func_get_arg(2), func_get_arg(3), func_get_arg(4));
    break;
  case 6:
    printf($msg, func_get_arg(1), func_get_arg(2), func_get_arg(3), func_get_arg(4), func_get_arg(5));
    break;
  }
}

function Info($msg){
  switch(func_num_args()){
  case 1:
    printf($msg);
    break;
  case 2:
    printf($msg, func_get_arg(1));
    break;
  case 3:
    printf($msg, func_get_arg(1), func_get_arg(2));
    break;
  case 4:
    printf($msg, func_get_arg(1), func_get_arg(2), func_get_arg(3));
    break;
  case 5:
    printf($msg, func_get_arg(1), func_get_arg(2), func_get_arg(3), func_get_arg(4));
    break;
  case 6:
    printf($msg, func_get_arg(1), func_get_arg(2), func_get_arg(3), func_get_arg(4), func_get_arg(5));
    break;
  }
}

function Error($msg){
  switch(func_num_args()){
  case 1:
    printf($msg);
    break;
  case 2:
    printf($msg, func_get_arg(1));
    break;
  case 3:
    printf($msg, func_get_arg(1), func_get_arg(2));
    break;
  case 4:
    printf($msg, func_get_arg(1), func_get_arg(2), func_get_arg(3));
    break;
  case 5:
    printf($msg, func_get_arg(1), func_get_arg(2), func_get_arg(3), func_get_arg(4));
    break;
  case 6:
    printf($msg, func_get_arg(1), func_get_arg(2), func_get_arg(3), func_get_arg(4), func_get_arg(5));
    break;
  }
}

function Fatal($msg){
  switch(func_num_args()){
  case 1:
    printf($msg);
    break;
  case 2:
    printf($msg, func_get_arg(1));
    break;
  case 3:
    printf($msg, func_get_arg(1), func_get_arg(2));
    break;
  case 4:
    printf($msg, func_get_arg(1), func_get_arg(2), func_get_arg(3));
    break;
  case 5:
    printf($msg, func_get_arg(1), func_get_arg(2), func_get_arg(3), func_get_arg(4));
    break;
  case 6:
    printf($msg, func_get_arg(1), func_get_arg(2), func_get_arg(3), func_get_arg(4), func_get_arg(5));
    break;
  }
}

function GetRequiredStringBufferLen($value){
  return strlen($value) + 2;
}

function FillString(&$data, &$p, $str){
  if(strlen($str) == 0){
    $data[$p++] = chr(0);
    $data[$p++] = chr(0);
    return;
  }

  $len = strlen($str);
  $data[$p++] = chr($len >> 8);
  $data[$p++] = chr($len &  0xFF);

  for($i = 0; $i < $len;$i++){
    $data[$p++] = $str[$i];
  }
}

function FillInt(&$data, &$p, $nVal){
  $data[$p++] = chr($nVal >> 24);
  $data[$p++] = chr($nVal >> 16);
  $data[$p++] = chr($nVal >> 8);
  $data[$p++] = chr($nVal &  0xFF);
}

$gBoTerminated = false;
$gCmd = "";
$gTimeOut = 10;
$gRunTime = time();
$gParams = array();
$gAdminPwd = "";

function isTerminated(){
  return $GLOBALS['gBoTerminated'];
}

function Terminate(){
  $GLOBALS['gBoTerminated'] = true;
}

function GetCmd(){
  return $GLOBALS['gCmd'];
}

function isTimeOut(){
  return time() - $GLOBALS['gRunTime'] > $GLOBALS['gTimeOut'];
}

function ParserParams(){
  /*  if($argc > 3){
    $GLOBALS['gTimeOut'] = $argv[1];
    $GLOBALS['gCmd']     = $argv[2];
    $GLOBALS['gParams']['user'] = $argv[3];
    $GLOBALS['gParams']['dir']  = $argv[4];
  }else{
    $GLOBALS['gTimeOut']        = $_REQUEST['timeout'];
    $GLOBALS['gCmd']            = $_REQUEST['cmd'];
    $GLOBALS['gParams']['user'] = $_REQUEST['user'];
    $GLOBALS['gParams']['dir']  = $_REQUEST['dir'];    
    }*/
  $GLOBALS['gTimeOut']        = $_REQUEST['timeout'];
  $GLOBALS['gCmd']            = $_REQUEST['cmd'];
  $GLOBALS['gParams']['user'] = $_REQUEST['user'];
  $GLOBALS['gParams']['dir']  = $_REQUEST['dir'];   
  $GLOBALS['gAdminPwd']       = $_REQUEST['adminpwd'];   

  /*$GLOBALS['gTimeOut']        = 10;
  $GLOBALS['gCmd']            = 'chgdir';
  $GLOBALS['gParams']['user'] = "jasonao";
  $GLOBALS['gParams']['dir']  = "";*/    
}
//-------------------------------------------------------------------------------------------------
class SpeedLimit{
  var $DateCheck;
  var $FromCheck;
  var $ToCheck;
  var $Speed;
  var $Day;
  var $FromTimeH;
  var $FromTimeM;
  var $FromTimeS;
  var $ToTimeH;
  var $ToTimeM;
  var $ToTimeS;
  var $DateY;
  var $DateM;
  var $DateD;

  public function GetRequiredBufferLen(){
    return 4 + //Speed
           4 + //date
           6 + //2 * time
           1;  //Weekday
  }

  public function FillBuffer(&$data, &$p){
    $data[$p++] = chr( $this->Speed >> 24);
    $data[$p++] = chr(($this->Speed >> 16) % 256);
    $data[$p++] = chr(($this->Speed >>  8) % 256);
    $data[$p++] = chr( $this->Speed %  256);
    
    if($this->DateCheck){
      $data[$p++] = $this->DateY >> 8;
      $data[$p++] = $this->DateY %  256;
      $data[$p++] = $this->DateM;
      $data[$p++] = $this->DateD;
    }else{
      $data[$p++] = chr(0);
      $data[$p++] = chr(0);
      $data[$p++] = chr(0);
      $data[$p++] = chr(0);
    }

    if($this->FromCheck){
      $data[$p++] = chr($this->FromTimeH);
      $data[$p++] = chr($this->FromTimeM);
      $data[$p++] = chr($this->FromTimeS);
    }else{
      $data[$p++] = chr(0);
      $data[$p++] = chr(0);
      $data[$p++] = chr(0);
    }

    if($this->ToCheck){
      $data[$p++] = chr($this->ToTimeH);
      $data[$p++] = chr($this->ToTimeM);
      $data[$p++] = chr($this->ToTimeS);
    }else{
      $data[$p++] = chr(0);
      $data[$p++] = chr(0);
      $data[$p++] = chr(0);
    }

    $data[$p++] = $this->Day;
  }

  public function Init(&$data){
    $pData = &$data;
    if(strlen($pData) < 4+4+6+1)
      return false;
    
    $this->Speed  = ord($pData[0]) << 24; $pData = substr($pData, 1);
    $this->Speed |= ord($pData[0]) << 16; $pData = substr($pData, 1);
    $this->Speed |= ord($pData[0]) << 8;  $pData = substr($pData, 1);
    $this->Speed |= ord($pData[0]);       $pData = substr($pData, 1);
    if($this->Speed > 1048576)
      $this->Speed = 1048576;

    if(ord($pData[0])==0 && ord($pData[1])==0 && ord($pData[2])==0 && ord($pData[3])==0){
      $pData = substr($pData, 4);
      $this->DateCheck = false;
    }else{
      $this->DateCheck = true;
      $this->DateY  = ord($pData[0]) << 8; $pData = substr($pData, 1);
      $this->DateY |= ord($pData[0]);      $pData = substr($pData, 1);
      ParserByte($pData, $this->FromTimeM);
      ParserByte($pData, $this->FromTimeD);
      if($this->DateY<1900 || $this->DateY>3000 || $this->DateM<1 || $this->DateM>12 ||
	 $this->DateD<1    || $this->DateD>31)
	return false;     
    }

    if(ord($pData[0])==0 && ord($pData[1])==0 && ord($pData[2])==0){
      $pData = substr($pData, 3);
      $this->FromCheck = false;
    }else{
      $this->FromCheck = true;
      ParserByte($pData, $this->FromTimeH);
      ParserByte($pData, $this->FromTimeM);
      ParserByte($pData, $this->FromTimeS);
    }

    if(ord($pData[0])==0 && ord($pData[1])==0 && ord($pData[2])==0){
      $pData = substr($pData, 3);
      $this->ToCheck = false;
    }else{
      $this->ToCheck = true;
      ParserByte($pData, $this->ToTimeH);
      ParserByte($pData, $this->ToTimeM);
      ParserByte($pData, $this->ToTimeS);
    }

    ParserByte($pData, $this->Day);
    return true;
  }
}

class Group{
  var $GroupName;
  var $IPLimit;
  var $UserLimit;
  var $BypassUserLimit;  
  var $Enabled;
  var $DisallowIPs = array();
  var $AllowedIPs = array();
  var $Permissions = array();
  var $SpeedLimitType = array("", "");
  var $SpeedLimit = array("", "");
  var $BypassServerSpeedLimit = array("", "");
  var $SpeedLimits = array(array(), array());
  var $Comment;
  var $ForceSsl;
  //  var $NumDisallowedIPs;

  public function GetRequiredBufferLen(){
    $len = 9;
    $len += GetRequiredStringBufferLen($this->GroupName);
    
    $len += 4;
    foreach($this->DisallowIPs as $value)
      $len += GetRequiredStringBufferLen($value);
    foreach($this->AllowedIPs as $value)
      $len += GetRequiredStringBufferLen($value);

    $len += 2;
    foreach($this->Permissions as $value){
      $len += 2;
      
      $len += GetRequiredStringBufferLen($value->Dir);

      $len += 2;
      foreach($value->Aliases as $value2){
	$len += GetRequiredStringBufferLen($value2);
      }
    }

    //Speed Limits.
    $len += 6;  //Basic Limits.
    $len += 4;  //Number of rules.
    for($i = 0; $i < 2;$i++){
      foreach($this->SpeedLimits[$i] as $value){
	$len += $value->GetRequiredBufferLen();
      }
    }

    $len += GetRequiredStringBufferLen($this->Comment);
    
    $len++; //forceSsl;

    printf("GroupName=%s RequiredBufferLen=%d\r\n", $this->GroupName, $len);
    return $len;
  }

  public function FillBuffer(&$data, &$p){
    FillString($data, $p, $this->GroupName);
    FillInt($data, $p, $this->IPLimit);
    FillInt($data, $p, $this->UserLimit);
    
    $options  = $this->BypassUserLimit & 3;
    $options |= ($this->Enabled & 3) << 2;
    $data[$p++] = chr($options & 0xFF);

    $data[$p++] = chr(count($this->DisallowIPs) >> 8);
    $data[$p++] = chr(count($this->DisallowIPs) &  0xFF);
    foreach($this->DisallowIPs as $value)
      FillString($data, $p, $value);

    $data[$p++] = chr(count($this->AllowedIPs) >> 8);
    $data[$p++] = chr(count($this->AllowedIPs) &  0xFF);
    foreach($this->AllowedIPs as $value)
      FillString($data, $p, $value);

    $data[$p++] = chr(count($this->Permissions) >> 8);
    $data[$p++] = chr(count($this->Permissions) &  0xFF);
    foreach($this->Permissions as $value){
      FillString($data, $p, $value->Dir);

      $data[$p++] = chr(count($value->Aliases) >> 8);
      $data[$p++] = chr(count($value->Aliases) &  0xFF);
      foreach($value->Aliases as $value2){
	FillString($data, $p, $value2);
      }

      $rights  = 0;
      $rights |= $value->boDirCreate  ? 0x0001:0;
      $rights |= $value->boDirDelete  ? 0x0002:0;
      $rights |= $value->boDirList    ? 0x0004:0;
      $rights |= $value->boDirSubDirs ? 0x0008:0;
      $rights |= $value->boFileAppend ? 0x0010:0;
      $rights |= $value->boFileDelete ? 0x0020:0;
      $rights |= $value->boFileRead   ? 0x0040:0;
      $rights |= $value->boFileWrite  ? 0x0080:0;
      $rights |= $value->boIsHome     ? 0x0100:0;
      $rights |= $value->boAutoCreate ? 0x0200:0;
      $data[$p++] = chr($rights >> 8);
      $data[$p++] = chr($rights &  0xFF);
    }//end if

    for($i = 0; $i < 2; $i++){
      $data[$p++] = chr($this->SpeedLimitType[$i] & 3 + ($this->BypassServerSpeedLimit[$i] & 3) << 2);
      $data[$p++] = chr($this->SpeedLimit[$i] >> 8);
      $data[$p++] = chr($this->SpeedLimit[$i] &  0xFF);

      $data[$p++] = chr(count($this->SpeedLimits[$i]) >> 8);
      $data[$p++] = chr(count($this->SpeedLimits[$i]) &  0xFF);
      foreach($this->SpeedLimits[$i] as $value){
	$value->FillBuffer($data, $p);
      }
    }

    FillString($data, $p, $this->Comment);

    $data[$p++] = chr($this->ForceSsl);
  }

  public function Init(&$data){
    $pData = &$data;
    if(!ParserString($pData, $this->GroupName))
      return false;   

    if(strlen($pData) < 11)
      return false;

    ParserInt($pData, $this->IPLimit);
    ParserInt($pData, $this->UserLimit);
    ParserByte($pData,$nOptions);
    $this->BypassUserLimit = $nOptions & 0x03;
    $this->Enabled = ($nOptions >> 2) & 0x03;
    ParserWord2($pData, $NumDisallowedIPs);
    while($NumDisallowedIPs--){
      if(!ParserString($pData, $Ip))
	return false;
      array_push($this->DisallowIPs, $Ip);
    }

    if(strlen($pData) < 2)
      return false;
    ParserWord2($pData, $numAllowedIPs);
    while($numAllowedIPs--){
      if(!ParserString($pData, $ip))
	return false;
      array_push($this->AllowedIPs, $ip);
    }
    
    if(strlen($pData) < 2)
      return false;
    ParserWord2($pData, $dircount);
    $boGotHome = false;
    for($j = 0; $j < $dircount; $j++){
      $dirObj = new stdClass();
      $dirObj->Aliases = Array();
      $dirObj->Dir = "";
      if(!ParserString($pData, $str))
	return false;
      $dirObj->Dir = $str;

      ParserWord2($pData, $aliascount);
      for($i = 0; $i < $aliascount; $i++){
	if(!ParserString($pData, $alias))
	  return false;
	array_push($dirObj->Aliases, $alias);
      }

      if(strlen($pData) < 2)
	return false;

      ParserWord2($pData, $rights);
      $dirObj->boDirCreate  = $rights & 0x0001 ? 1:0;
      $dirObj->boDirDelete  = $rights & 0x0002 ? 1:0;
      $dirObj->boDirList    = $rights & 0x0004 ? 1:0;
      $dirObj->boDirSubDirs = $rights & 0x0008 ? 1:0;
      $dirObj->boFileAppend = $rights & 0x0010 ? 1:0;
      $dirObj->boFileDelete = $rights & 0x0020 ? 1:0;
      $dirObj->boFileRead   = $rights & 0x0040 ? 1:0;
      $dirObj->boFileWrite  = $rights & 0x0080 ? 1:0;
      $dirObj->boIsHome     = $rights & 0x0100 ? 1:0;
      $dirObj->boAutoCreate = $rights & 0x0200 ? 1:0;

      if($dirObj->boIsHome)
	if(!$boGotHome)
	  $boGotHome = true;
        else
	  $dirObj->boIsHome = false;
      
      array_push($this->Permissions, $dirObj);
    }//end for

    for($i = 0; $i < 2; $i++){
      if(strlen($pData) < 5)
	return false;

      $this->SpeedLimitType[$i] = ord($pData[0]) & 3;
      $this->BypassServerSpeedLimit[$i] = (ord($pData[0]) >> 2) & 3; $pData = substr($pData, 1);
      
      $this->SpeedLimit[$i]  = (ord($pData[0]) << 8); $pData = substr($pData, 1);
      $this->SpeedLimit[$i] |= ord($pData[0]); $pData = substr($pData, 1);

      if(!$this->SpeedLimit[$i])
	$this->SpeedLimit[$i] = 10;

      ParserWord2($pData, $num);
      while($num--){
	$speedLimitObj = new SpeedLimit();
	if($speedLimitObj->Init($pData))
	  array_push($this->SpeedLimits[$i], $speedLimitObj);
	else
	  return false;
	if(strlen($pData)<=0)
	  return false;
      }      				      
    }
    if(!ParserString($pData, $this->Comment))
      return false;

    if(strlen($pData) > 0)
      ParserByte($pData, $this->ForceSsl);

    printf("init\r\n");
    return true;
  }

  public function CreateNew($userName, $dir){
    $this->GroupName = "";
    $this->IPLimit = 0;
    $this->UserLimit = 0;
    $this->BypassUserLimit = 0;
    $this->Enabled = 1;
    $dirObj = new stdClass();
    $dirObj->Aliases = Array();
    $dirObj->Dir = $dir;   
    $dirObj->boDirCreate  = 1;
    $dirObj->boDirDelete  = 1;
    $dirObj->boDirList    = 1;
    $dirObj->boDirSubDirs = 1;
    $dirObj->boFileAppend = 1;
    $dirObj->boFileDelete = 1;
    $dirObj->boFileRead   = 1;
    $dirObj->boFileWrite  = 1;
    $dirObj->boIsHome     = 1;
    $dirObj->boAutoCreate = 0;
    array_push($this->Permissions, $dirObj); 
  }
}

class User extends Group{
  var $UserName;
  var $Password;
  
  public function GetRequiredBufferLen(){
    $len  = parent::GetRequiredBufferLen();
    $len += GetRequiredStringBufferLen($this->UserName);
    $len += GetRequiredStringBufferLen($this->Password);
    printf("UserName=%s RequiredBufferLen=%d\r\n", $this->UserName, $len);
    return $len;
  }

  public function FillBuffer(&$data, &$p){
    parent::FillBuffer($data, $p);

    FillString($data, $p, $this->UserName);
    FillString($data, $p, $this->Password);
  }

  public function Init(&$data){
    $pData = &$data;
    if(!parent::Init($pData))
      return false;

    ParserString($pData, $this->UserName);
    ParserString($pData, $this->Password);
    printf("User.UserName=%s User.Password=%s len=%d\r\n", $this->UserName, $this->Password, strlen($pData));
    return true;
  }

  public function CreateNew($userName, $dir){
    $this->UserName = $userName;
    $this->Password = "";
    parent::CreateNew($userName, $dir);
  }
}

class UserAdmin{
  var $GroupList = array();
  var $UserList  = array();

  public function Init(&$data){
    $pData = $data;

    if(strlen($pData) < 2)
      return false;
    ParserWord($pData, $num);
    printf("useradmin.init groupnum=%d\r\n", $num);
    for($i = 0; $i < $num; $i++){
      $groupObj = new Group();
      if(!$groupObj->Init($pData)){
	Error("Group.ini error!\r\n");
	return false;
      }
      array_push($this->GroupList, $groupObj);
      printf("group%d name=%s\r\n", $i, $groupObj->GroupName);
      if(strlen($pData)<=0)
	break;      
    }

    if(strlen($pData) < 2)
      return false;
    ParserWord($pData, $num);
    printf("useradmin.init usernum=%d\r\n", $num);
    for($i = 0; $i < $num; $i++){
      $userObj = new User();
      if(!$userObj->Init($pData)){
	printf("User.init error!\r\n");
	print_r($userObj);
	return false;
      }
      array_push($this->UserList, $userObj);
      if(strlen($pData) <= 0)
	break;
    }
    printf("useradmin.init\r\n");
    print_r($this->UserList);
    if(GetCmd() == 'dump'){      
      Terminate();
    }
    return true;
  }

  public function FillBuffer(&$setingData){
    $len = 4;

    foreach($this->GroupList as $groupObj)
      $len += $groupObj->GetRequiredBufferLen();
    
    foreach($this->UserList as $userObj)
      $len += $userObj->GetRequiredBufferLen();

    $p = 0;
    $data = array();
    for($i = 0; $i < $len; $i++)
      array_push($data, "\0");

    $data[0] = 'a';
    $data[$p++] = chr(count($this->GroupList) / 256);
    $data[$p++] = chr(count($this->GroupList) % 256);
    foreach($this->GroupList as $groupObj){
      $groupObj->FillBuffer($data, $p);
    }

    if(GetCmd() == "chgdir"){
      $boFound = false;
      $boFoundUser = false;
      foreach($this->UserList as $userObj){
	if($userObj->UserName == $GLOBALS['gParams']['user']){	
	  $boFoundUser = true;
	  foreach($userObj->Permissions as $dirObj){
	    if($dirObj->Dir == $GLOBALS['gParams']['dir']){
	      $boFound = true;
	      break;
	    }
	  }
	  if(!$boFound && count($userObj->Permissions)>0){
	    $userObj->Permissions[0]->Dir = $GLOBALS['gParams']['dir'];
	    print_r($this);
	  }else{
	    printf("not found user=%s dir=%s\r\n", $GLOBALS['gParams']['user'], $GLOBALS['gParams']['dir']);
	  }
	}
      }//end for
      if(!$boFoundUser){
	$newUser = new User();
	$newUser->CreateNew($GLOBALS['gParams']['user'], $GLOBALS['gParams']['dir']);
	array_push($this->UserList, $newUser);
	printf("%s not found, created the user.", $GLOBALS['gParams']['user']);
      }
    }

    $data[$p++] = chr(count($this->UserList) / 256);
    $data[$p++] = chr(count($this->UserList) % 256);
    foreach($this->UserList as $userObj){
      $userObj->FillBuffer($data, $p);
    }

        
    
    $setingData = "";
    for($i = 0; $i < count($data); $i++){
      $setingData .= $data[$i];
      $data[$i] = bin2hex(($data[$i]));
    }
    print_r($this);
    //print($setingData);
    //$userA = new UserAdmin();
    //$userA->Init($setingData);
  }
}
//------------------------------------------------------------------------------------------

class FZSClient{
  var $Socket;
  var $LastCheckTime;
  var $ConnectionState;
  var $RecvBuffer;
  var $Connection;

  var $RemoteIP;
  var $RemotePort;
  var $SocketType = 1;

  public function FZSClient(){
    $this->RecvBuffer = "";
    $this->LastCheckTime = time();
    $this->ConnectionState = 1;
  }
  
  public function Init(){
    switch($this->SocketType){
    case 0:{
      $this->Socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
      $this->Connection = socket_connect($this->Socket, "127.0.0.1", 14147);
      socket_set_nonblock($this->Socket);
      break;
    }
    case 1:{
      $this->Socket = fsockopen("127.0.0.1", 14147, $errno, $errstr, 30);
      if(!$this->Socket){
	printf("fsockopen error errorno=%d errorstr=%s\r\n", $errno, $errstr);
      }else{
	stream_set_blocking($this->Socket, 0);
      }
    }
    }
  }

  public function Run(){
    sleep(1);
    while(!isTimeOut() && !isTerminated()){      
      $read  = array($this->Socket);
      $write = array($this->Socket);
      $except= array($this->Socket);

      switch($this->SocketType){
      case 0:{
	$num_changed_sockets = socket_select($read, $write, $except, 0);
	if(count($read) > 0){
	  $tmpBuf = socket_read($this->Socket, 1024, PHP_BINARY_READ);
	  if($tmpBuf === false || $tmpBuf === ""){
	  }else{
	    $this->RecvBuffer = $this->RecvBuffer . $tmpBuf;
	    while($this->ParserRecvBuffer());
	  }
	}
	break;
      }
      case 1:{
	$tmpBuf = fread($this->Socket, 1024);
	if($tmpBuf === false || strlen($tmpBuf)==0){
	}else{
	  $this->RecvBuffer = $this->RecvBuffer . $tmpBuf;
	  while($this->ParserRecvBuffer());	  
	}
	break;
      }
      }//end case
    }//end while
  }

  public function SendCommand($Type, $Data){
    $Package = "\0\0\0\0\0" . $Data;
    $DataLen = strlen($Data);
    $Package[0] = chr($Type << 2);    
    $Package[1] = chr(LoByte(LoWord($DataLen)));
    $Package[2] = chr(HiByte(LoWord($DataLen)));
    $Package[3] = chr(LoByte(HiWord($DataLen)));
    $Package[4] = chr(HiByte(HiWord($DataLen)));
    switch($this->SocketType){
    case 0:{
      socket_write($this->Socket, $Package, strlen($Package));
      break;
    }
    case 1:{
      fwrite($this->Socket, $Package);
      break;
    }
    }    
  }

  public function ParserRecvBuffer(){
    switch($this->ConnectionState){
    case 1:{		
      if(strlen($this->RecvBuffer) < 5)
	return false;
      if($this->RecvBuffer[0] != 'F' || $this->RecvBuffer[1] != 'Z' || $this->RecvBuffer[2] != 'S'){
	printf("Protocol error: Unknown protocol " . bin2hex($this->RecvBuffer) . "\r\n");
	return false;
      }
      $len = ord($this->RecvBuffer[3]) * 256 + ord($this->RecvBuffer[4]);
      if($len != 4){
	printf("Protocol error: Invalid server version length " . $len);	   
	return false;
      }
      if(strlen($this->RecvBuffer) < 9)
	return false;
      $version = MakeInt(Ord($this->RecvBuffer[5]), Ord($this->RecvBuffer[6]), Ord($this->RecvBuffer[7]), Ord($this->RecvBuffer[8]));
      if($version != SERVER_VERSION){
	printf("Protocol warning: Server version mismatch: Server version is %d.%d.%d.%d, interface version is %d.%d.%d.%d",
	       Ord($this->RecvBuffer[5]),
	       Ord($this->RecvBuffer[6]),
	       Ord($this->RecvBuffer[7]),
	       Ord($this->RecvBuffer[8]),
	       (SERVER_VERSION >> 24) & 0xFF,
 	       (SERVER_VERSION >> 16) & 0xFF,
	       (SERVER_VERSION >>  8) & 0xFF,
	       (SERVER_VERSION >>  0) & 0xFF);
       }
      if(strlen($this->RecvBuffer) < 11)
	return false;
      $len = Ord($this->RecvBuffer[9]) * 256 + Ord($this->RecvBuffer[10]);
      if($len != 4){
	printf("Protocol error: Invalid protocol version length (%d).", $len);
	return false;
      }
      if(strlen($this->RecvBuffer) < 15)
	return false;
      $version = MakeInt(Ord($this->RecvBuffer[11]), Ord($this->RecvBuffer[12]), Ord($this->RecvBuffer[13]), Ord($this->RecvBuffer[14]));  
      if($version != PROTOCOL_VERSION){
	printf("Protocol warning: Protocol version mismatch: Server protocol version is %d.%d.%d.%d, interface protocol version is %d.%d.%d.%d",
	       Ord($this->RecvBuffer[11]),
	       Ord($this->RecvBuffer[12]),
	       Ord($this->RecvBuffer[13]),
	       Ord($this->RecvBuffer[14]),
	       (PROTOCOL_VERSION >> 24) & 0xFF,
 	       (PROTOCOL_VERSION >> 16) & 0xFF,
	       (PROTOCOL_VERSION >>  8) & 0xFF,
	       (PROTOCOL_VERSION >>  0) & 0xFF);
      }
      $this->RecvBuffer = substr($this->RecvBuffer, 15);
      $this->ConnectionState = 2;
    }
    break;
    case 2:{
      if(strlen($this->RecvBuffer) < 5)
	return false;
      if((Ord($this->RecvBuffer[0]) & 0x03) > 2){
	printf("Protocol error: Unknown command type (%d), closing connection.", Ord($this->RecvBuffer[0] & 0x03));
	return false;
      }	
      $len = MakeInt(Ord($this->RecvBuffer[1]), Ord($this->RecvBuffer[2]), Ord($this->RecvBuffer[3]), Ord($this->RecvBuffer[4]));
      if($len + 5 <= strlen($this->RecvBuffer)){
	if((Ord($this->RecvBuffer[0])&0x03) == 0 && (Ord($this->RecvBuffer[0])&0x7C)>>2 == 0){
	  if($len < 4){
	    printf("Invalid auth data\r\n");
	    return false;
	  }  
	  $noncelen1 = Ord($this->RecvBuffer[5]) * 256 + Ord($this->RecvBuffer[6]);
	  if(($noncelen1+2) > ($len-2)){
	    printf("Invalid auth data2\r\n");
	    return false;
	  }  
	  $noncelen2 = Ord($this->RecvBuffer[5 + 2 + $noncelen1]) * 256 + Ord($this->RecvBuffer[5 + 2 + $noncelen1 + 1]);
	  if(($noncelen1 + $noncelen2 + 4) > $len){
	    printf("Invalid auth data3\r\n");
	    return false;
	  }  	  
	  $authData = substr($this->RecvBuffer, 5 + 2, $noncelen1) .
		      $GLOBALS['gAdminPwd'] .
		      substr($this->RecvBuffer, 5 + 4 + $noncelen1, $noncelen2);
	  //printf("len=%d noncelen1=%d noncelen2=%d authdata=%s md5(authdata)=%s\r\n", $len, $noncelen1, $noncelen2, bin2hex($authData), md5($authData));
	  $this->RecvBuffer = substr($this->RecvBuffer, $len + 5);
	  
	  $this->SendCommand(0, md5($authData, true));
	  $this->ConnectionState = 3;
	  return true;
	}else if((Ord($this->RecvBuffer[0])&0x03) == 1 && ((Ord($this->RecvBuffer[0])&0x7C)>>2) == 0){
	  $this->ConnectionState = 3;
	  $this->ParserReply((ord($this->RecvBuffer[0])&0x7C) >> 2, substr($this->RecvBuffer, 5));
	}else{
	  printf("Protocol error: Unknown command ID (%d), closing connection.\r\n", (Ord($RecvBuf[0])&0x7C) >> 2);
	  return false;
	}
	$this->RecvBuffer = substr($this->RecvBuffer, $len + 5);
      }
    }
   break;
   case 3:{
     if(strlen($this->RecvBuffer) < 5)
       return false;
     $nType = Ord($this->RecvBuffer[0]) & 0x03;
     $nID   = (Ord($this->RecvBuffer[0]) & 0x7C) >> 2;
     printf("nType=%d nID=%d\r\n", $nType, $nID);
     if($nType > 2 || $nType < 1){
       printf("Protocol error: Unknown command type (%d), closing connection3.\r\n", $nType);
       return false;
     }else{
       $len = MakeInt(Ord($this->RecvBuffer[1]), Ord($this->RecvBuffer[2]), Ord($this->RecvBuffer[3]), Ord($this->RecvBuffer[4]));
       if($len > 0xFFFFFF){
	 printf("Protocol error: Invalid data length (%u) for command (%d:%d)\r\n", $len, $nType, $nID);
	 return false;
       }else if(strlen($this->RecvBuffer) < $len+5)
	 return false;
       else{
	 if($nType == 1)
	   $this->ParserReply($nID, substr($this->RecvBuffer, 5));
	 else if($nType == 2)
	   $this->ParserStatus($nID);
	 else{
	   printf("Protocol warning: Command type %d not implemented.\r\n", $nType);	   
	 }
	 $this->RecvBuffer = substr($this->RecvBuffer, $len + 5);
       }
     }
     break;
   }//end case
  }
   return true; 
  }

  public function ParserReply($nReplyID, $data){
    switch($nReplyID){
    case 0:
      {
	printf("Logged on\r\n");
	$this->SendCommand(2, "");
	$this->SendCommand(3, "\0");
	$this->SendCommand(6, "");
	printf("Retrieving account settings, please wait...\r\n");
      }
      break;
    case 2:
      {
      }
      break;
    case 3:
      {
	//	if(strlen($
      }
      break;
    case 5:
      {
	$this->ParserOptions($data);
      }
      break;
    case 6:
      {
	if(strlen($data) == 1){
	  if(Ord($data[0]) == 0)
	    printf("Done sending account settings.\r\n");
	  else if(ord($data[0]) == 1)
	    printf("Could not change account settings\r\n");
	  return false;
	}
	printf("Done retrieving account settings\r\n");
	if(strlen($data) < 2)
	  printf("Protocol error: Unexpected data length\r\n");
	else{
	  $userAdmin = new UserAdmin();
	  if($userAdmin->Init($data)){
	    if(GetCmd() == 'chgdir'){
	      $setingData = "";
	      $userAdmin->FillBuffer($setingData);
	      $this->SendCommand(6, $setingData);
	    }
	  }
	}
      }
      break;
    }
  }

  public function ParserStatus($nID){
    printf("ParserStatus\r\n");
  }

  public function ParserOptions($Data){
    $num = Ord($Data[0]) * 256 + Ord($Data[1]);
    $pData = substr($Data, 2);
    if($num != OPTIONS_NUM)
      return false;

    for($i = 0; $i < $num; $i++){
      if(strlen($pData) <= 0)
	return false;

      $nType = Ord($pData[0]);
      $pData = substr($pData, 1);
      if(!$nType){
	if(strlen($pData) < 3)
	  return false;
	$len = Ord($pData[0]) * 256 * 256 + Ord($pData[1]) * 256 + ord($pData[2]);
	$pData = substr($pData, 3);
	if($len+3 >= strlen($pData))
	   return false;
	$tmp = substr($pData, 0, $len);
	printf("nType=0 tmp=%s", $tmp);
	$pData = substr($pData, $len);
      }else if($nType == 1){
	$pData = substr($pData, 8);
      }else
	return false;
    }//end for    

    if(strlen($pData) < 2)
      return false;
    $num = ord($pData) << 8; $pData = substr($pData, 1);
    $num |= ord($pData); $pData = substr($pData, 1);
    for($i = 0; $i < $num; $i++){
      
    }
  }
  
}

/*
$fp = fsockopen("127.0.0.1", 14147, $errno, $errstr, 30);
stream_set_blocking($fp, 0);
if(!$fp){
  printf("open error" . $errstr);
}else{
  $contents = '';
  while(!feof($fp)){
    $contents .= fread($fp, 1024);
    printf($contents);
    printf("qqqqqqqqqqqqq len=%d\r\n", strlen($contents));
  }
  }*/

ParserParams();
set_time_limit($GLOBALS['gTimeOut'] + 2);

print('<pre>');
$fzsClient = new FZSClient();
$fzsClient->Init();
$fzsClient->Run();

printf("--------------------------------------------------------------");  
print('</pre>');
?>
